#include "uart_device.h"
#include "stm32f1xx_hal.h"
#include "FreeRTOS.h"
#include "semphr.h"

#include <string.h>

// Defines the length of the UART receive queue  
#define UART_RX_QUEUE_LEN 100

// External reference to the HAL UART handle  
extern UART_HandleTypeDef huart1;

// Device-specific private data structure  
struct UART_Device g_stm32_uart1;

struct UART_Data
{
    UART_HandleTypeDef *handle; 	// Pointer to the UART HAL handle  
    SemaphoreHandle_t  xTxSem;      // Transmit semaphore for synchronization  
    QueueHandle_t xRxQueue;         // Receive queue for storing received data  
    uint8_t rxdata;                 // Buffer for received data  

};

// Interrupt callback for transmit completion  
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
    struct UART_Data *data;
    
    if(huart == &huart1)    
    {
        data = g_stm32_uart1.priv_data;
        
        /* Release semaphore. */
        xSemaphoreGiveFromISR(data->xTxSem, NULL);
    }
    
}

// Interrupt callback for receive completion 
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	struct UART_Data *uart_data;

	if(huart == &huart1)
	{
		uart_data = g_stm32_uart1.priv_data;
		
		/* Send received data to the queue. */  
        xQueueSendFromISR(uart_data->xRxQueue, &uart_data->rxdata, NULL);  
  
        /* Restart UART receive to fill the queue continuously. */  
        HAL_UART_Receive_IT(uart_data->handle, &uart_data->rxdata, 1);  
	}
}

// Initialization functions for the STM32 UART device  
// These functions belong to the device layer as they interact with specific hardware and RTOS resources  
static int stm32_uart_init(struct UART_Device * pDev, int baud, int datas, char parity, int stop)
{
    struct UART_Data *uart_data = pDev->priv_data;
    
    uart_data->xTxSem = xSemaphoreCreateBinary();
	uart_data->xRxQueue = xQueueCreate(UART_RX_QUEUE_LEN, 1);

	HAL_UART_Receive_IT(uart_data->handle, &uart_data->rxdata, 1);
	
    return 0;
}

static int stm32_uart_send(struct UART_Device * pDev, uint8_t *datas, int len, int timeout_ms)
{
    struct UART_Data *uart_data = pDev->priv_data;  
      
    /* Start UART transmission to trigger an interrupt. */  
    HAL_UART_Transmit_IT(uart_data->handle, datas, len);  
      
    /* Wait until the transmission is complete. */  
    if (pdTRUE == xSemaphoreTake(uart_data->xTxSem, timeout_ms))  
    {  
        return 0;  
    }  
    else  
    {  
        return -1;  
    }  
}

/**  
 * @brief Receive data from a UART device.  
 *  
 * @param pDev Pointer to the UART device structure.  
 * @param data Pointer to the buffer where the received data will be stored.  
 * @param timeout_ms Timeout duration for receiving data, in milliseconds.  
 * @return Returns 0 upon success, -1 upon failure.  
 */ 
static int stm32_uart_recv(struct UART_Device * pDev, uint8_t *data, int timeout_ms)
{
	struct UART_Data *uart_data = pDev->priv_data;

    if(pdPASS == xQueueReceive(uart_data->xRxQueue, data, timeout_ms))
    {
		return 0;
	}
	else
	{
		return -1;
	}
	
    return 0;
}

static struct UART_Data g_stm32_uart1_data =
{
    &huart1,
};

static struct UART_Device g_stm32_uart1 = 
{
    "stm32_freertos_uart1",
    stm32_uart_init,
    stm32_uart_send,
    stm32_uart_recv,
    &g_stm32_uart1_data,
};

/******************************************************/

static int stm32_bear_uart_init(struct UART_Device * pDev, int baud, int datas, char parity, int stop)
{
    return 0;
}

static int stm32_bear_uart_send(struct UART_Device * pDev, uint8_t *datas, int len, int timeout_ms)
{
	return HAL_UART_Transmit((UART_HandleTypeDef*)pDev->priv_data, datas, len, timeout_ms);  
}

static int `(struct UART_Device * pDev, uint8_t *data, int timeout_ms)
{
	return HAL_UART_Receive((UART_HandleTypeDef*)pDev->priv_data, data, 1, timeout_ms);  
}

static struct UART_Device g_stm32_bear_uart1 = 
{
    "stm32_bear_uart1",
    stm32_bear_uart_init,
    stm32_bear_uart_send,
    stm32_bear_uart_recv,
    &huart1,
};


struct UART_Device *g_uart_devs[] = {&g_stm32_uart1, &g_stm32_bear_uart1};

struct UART_Device *GetUARTDevice(char *name)
{
	int i = 0;

	for(i=0; i<sizeof(g_uart_devs)/sizeof(g_uart_devs[0]); i++)
	{
		if(0 == strcmp(name, g_uart_devs[i]->name))
		{
			return g_uart_devs[i];
		}
	}
	
	return NULL;
		
}


